Ink
What is Ink?
In a few words, it’s an open-source scripting language for interactive narratives. It can be used to develop games which is the major focus of this documentation section. More info can be found on the official website.
Before You Start
While reading the official documentation, you might get overwhelmed by Inkle’s unique terms and concepts. Despite being called Ink, it is less about text fluid and more closely related to its creator’s namesake i.e. Inkle. An inkle is a narrow linen tape often used to make shoelaces. Hopefully knowing this makes lingo like knots and threads a little less confusing.
Articles
- Metroidvania Pacing Charts in Ink
- Ink Numeric Input
- Ink Text Logs
- Making a World Map in Ink
- Mixing Ingredients in Ink
- Running Inky on a Chromebook
- Prototyping a Card Swiping Game in Ink
- Mapping a PAD Model in Ink
- Ink Text Generation with Context-Free Grammar
- Character Pronouns in Ink
- Tabular Data in Ink
- Recursive Functions in Ink
- Testing Strategies using Inky Web Export
Lingo
➥Operators
Operator | Purpose | Example | Notes |
---|---|---|---|
// | Single line comment | // Explaining why not describing what | Content after // is ignored by the Ink compiler for just that line. |
/* */ | Multiple line comment | /* Not much is going on here indeed */ |
Content between /* and */ is ignored by the Ink compiler. |
# | Tagging | I didn’t do it. #VO_ID_123 | Multiple tags per line are allowed, meant for metadata purposes. |
* | Choice/Option | The phone rings. * Answer * Ignore |
Input mechanism for making decisions. Once used, it is consumed and never offered again. |
+ | Sticky Choice/Option | The phone rings again. + Answer + Ignore |
A choice that never gets consumed. It “sticks” around. |
[] | Text Inline Skip/Break (for Choices only) |
* Jump[?! It’s risky tho…]ing off the cliff was a bad idea. // Choice: Jump?! It’s risky tho… // Response: Jumping off the cliff was a bad idea. |
Text presented between the square brackets is shown in the choice text but not in its response. Text after the square brackets is not shown in the choice and only in the response. This technique is sometimes referred to as Weave-Style. |
<> | Prevent line breaks | I didn’t know <> what to do! |
Called “glue,” it sticks lines together. Angle brackets must be next to each other, no spaces in between! Can be used at the end of the first line or beginning of the second. |
{} | Print variables or expressions | VAR dog = “Odie” VAR cat = “Garfield” {dog + “ and “ + cat} // Prints: Odie and Garfield { 1 > 100 } // Prints: false |
Useful for exposing variable values to the reader. Can also be used for debugging purposes. Cannot print text content unless you wrap it as a string. |
{ | } | Variable Text | { First Time! | Welcome back! } Good {& Morning | Afternoon | Evening } to you. {! Try the fish. | Now, try the stew. | Last one, try the pie! } Seeing you makes us {~ happy| glad| joyful}! |
Called “alternatives,” | separators split text between braces into chunks and shows them:* Sequences (no prefix) - in order and holds the last one * Cycles (&) - in order and loops back to the first * Once-Only (!) - in order and holds the last one as blank * Shuffle (~) - randomly Can be nested and used in choices. See docs. |
{ : } | Inline Condition | VAR stat = 9001 { stat > 9000 : It’s over 9000!! } It’s a { stat <= 177 : | mis}match. |
Evaluates an expression and outputs the following text if it’s true. If a | separator is used to split the text, only the second chunk is used if false. |
* { } | Conditional Choice | + { HP > 0 } Fight + { HP > 10 } { item_count > 0} Use Item * { RANDOM(1,12) <= 6 } Flee |
Activates or deactivates a choice (sticky or not) based on a conditional check. Multiple conditions can be used, and their expressions can also utilize logical operators |
== | Section of Text Content | == section1 -> DONE === section2 === -> DONE |
Called “knots,” these mark sections of text and give them a label for redirection purposes. Sections need to flow into other sections or terminate properly with -> DONE or -> END |
= | Sub-section of Text Content | === section1 = subsection1 content |
Called “stitches,” these can break up knots to manage huge amounts of text. Stitches have their own labels and other knots can redirect to them. |
-> | Go to a specific section | === section1 Section 1 -> section2 |
Called “diverts,” these change the flow of the story by redirecting it. |
<- | Combine multiple sections into one | <- section1 <- section2 === section1 Section 1 === section2 Section 2 |
Called “threads,” these collect content into a single chunk with collected options at the bottom. It’s a powerful way of combining content and choices. |
- | Capture story flow | * Decide Left * Decide Right - Pointless. You can only go forward! |
Called “gathers,” these redirect the story to itself. It’s a type of fallback mechanism often used to create chokepoints for choices that do not divert. |
( ) | Labels gathers and choices | * (picked_left) Decide Left * Decide Right - (feedback) You were { picked_left : correct!| wrong…} + [Can you repeat that?] -> feedback * [I’m done.] Goodbye. -> DONE |
Allows testing and diverting on gathers and options. If diverted to a choice, it is immediately chosen. |
~ | Denotes an expression | VAR found = false I found the ruby! ~ found = true |
Sets a line as code to be evaluated instead of text content |
+ - * / % | Math operators | You can use mod instead of % |
|
== != >= <= < > | Comparison operators | ||
&& || | AND/OR Logic operators | You can use and instead of && .You can use or instead of || . |
|
! | NOT operator | VAR read = true VAR status = true ~ status = !read { status } // false { !status } // status (once-only) { not status } // true |
Negates an expression. You can use not instead of ! , sometimes necessary when used to print with {} . |
? | Has check | VAR status = “Diplomatic Immunity” { status ? “Immunity” } // outputs true |
Checks if a list variable has a specific flag set. Checks if a string contains another string (substring). You can use has instead of ? . |
!? | “Has not” check | Negation of Has check. You can use hasnt instead of !? . |
|
^ | Intersection check | LIST parts = thumb, belly, tail VAR dog = (belly, tail) VAR human = (thumb, belly) { dog ^ human } // outputs belly |
Checks if two list variables have overlapping values. Returns a list variable. |
➥Keywords
Keyword | Purpose | Example | Notes |
---|---|---|---|
CONST | Defines a global constant | CONST PI = 3.1415 | |
VAR | Defines a global variable | VAR x = “spot” | Always hoisted and initialized once. Can initialize to a constant but not to other variables nor expressions. |
temp | Defines a temporary variable | ~ temp doublePI = PI * 2 | Only exists in the knot/stitch it was defined in. Needs ~ to be evaluated. |
LIST | Defines an Ink List | LIST weekdays = Mon, Tues, Wed, Thurs, Fri | Lists are variables that are Boolean sets as well as enums. Read the official docs for more info. |
function | Defines an Ink function | VAR item_count = 10 You were robbed. {get_robbed()} // function call You have {item_count} items. // outputs 0 == function get_robbed() ~ item_count = 0 |
Functions are knots that can call other functions and return values. They do not support diverts, choices and stitches. Labelled gathers are supported. Since they are knots, == needs to be prefixed.Functions are called with () within ~ or {} , the latter prints the return value, if any. |
return | Function return | { square(10) } // outputs 100 === function square(x) ~ return x * x |
Stops function evaluation and returns a value. Needs ~ to be processed. |
else | Condition block run if all fails | { RANDOM(1,6) > 3 : You win. - else: You lose. } |
Acts like else in if-else statement. Acts like default in switch-case statement. |
true, false | Values of boolean variables | Reserved keywords but only in lowercase. So “True” is okay for a knot-name. | |
INCLUDE | Combines an Ink file with the current one. | INCLUDE test.ink | Knot and global variable names must be unique or duplicate errors will be reported. |
EXTERNAL | Defines a game engine function | EXTERNAL GetTime() | It’s a good idea to implement a fallback function in Ink so that lines that call external functions can run correctly outside of the game engine. |